
/*
为了让电脑知道“接下来的字要用红色”，你需要在字前面写一个特殊的指令（就像咒语）。这个指令长得像这样：\x1B[38;5;1m。

\x1B[：这是所有颜色指令的开头，告诉电脑：“注意！颜色指令来了！”
38;5;1：这部分是具体说明。
  38;5 意思是“我要设置前景色（就是字的颜色）”，
  1 意思是“颜色代码是1”（代码1代表红色）。
m：这是指令的结尾，表示“说完了”。

所以，\x1B[38;5;1m 就是“把字变成红色”的魔法咒语。
但是，你肯定不希望后面所有字都是红色的。所以，你还需要一个“取消颜色”的咒语：\x1B[0m。这个咒语会让颜色变回默认的白色。
*/

class AnsiColor{
  /// ansi 颜色控制字符
  static const String ansiEsc = '\x1B[';
  
  /// 默认颜色
  static const String ansiDefault = '${ansiEsc}0m';

  final int? fg;
  final int? bg;
  final bool color;

  /// flutter 函数掉用后面加 : 表示：
  /// 在构造函数的主代码体执行之前，
  /// 用来初始化类的 final 变量（必须在构造时确定值的变量）或者调用父类的构造函数。
  /// 所以final 变量必须在:前初始化，初始化后不能再改变。
  AnsiColor.none()
    : fg = null,
      bg = null,
      color = false;

  // 前景色 
  // Dart 中 传入参数赋值给 fg 变量
  AnsiColor.fg(this.fg)
    : bg = null,
      color = true;

  AnsiColor.bg(this.bg)
    : fg = null,
      color = true;

  /*
   * 颜色转换
   *
    红色: 1 (标准红), 196 (最亮的红)
    绿色: 2 (标准绿), 46 (最亮的绿)
    黄色: 3 (标准黄), 226 (亮黄/金色)
    蓝色: 4 (标准蓝), 21 (最亮的蓝)
    品红 (Magenta): 5 (标准品红), 201 (亮品红)
    青色 (Cyan): 6 (标准青), 51 (亮青)
    白色: 7 (标准白), 231 (最亮的白)
    黑色: 0 (标准黑), 16 (最暗的黑灰)
    灰色: 232 到 255，数字越大颜色越亮。            
  */
  @override
  String toString() {
    ///搜索 256色 ANSI 转换器 可以查看颜色改变fg的值来改变颜色
    ///https://aratool.io/zh/conversion/color/
    if (fg != null) {
      return '${ansiEsc}38;5;${fg}m';
    }
    if (bg != null) {
      return '${ansiEsc}48;5;${bg}m';
    }
    return ansiDefault;
  }


  String call(String msg) {
    if (color) {
      // this 代表当前对象 AnsiColor 实例
      return '$this$msg$ansiDefault';
    }
    return msg;
  }

  AnsiColor toFg() => AnsiColor.bg(bg);


  AnsiColor toBg() => AnsiColor.fg(fg);


  ///get: 这表示 resetForeground 不是一个方法（不需要 ()），
  ///而是一个计算属性 (computed property)。
  ///你可以像访问变量一样访问它（myColor.resetForeground），
  ///但它背后是执行一段代码来计算值。
  String get resetForeground => color ? '${ansiEsc}39m' : '';

  String get resetBackground => color ? '${ansiEsc}49m' : '';


  static int grey(double level) {
    ///assert 是一个断言。它检查 level 是否在 0 到 1 之间。
    // assert(level >= 0 && level <= 1);
    // return (232 + (level * 23)).toInt();

    /// clamp 方法确保 level 在指定范围内（0 到 1）。
    /// 如果 level 小于 0，就返回 0；如果大于 1，就返回 1；否则返回 level 本身。
    /// clamp 方法返回的是一个 num 类型，所以需要调用 round() 方法将其四舍五入为整数。
    /// 最后加上 232 得到最终的灰色代码。
    return 232 + (level.clamp(0, 1) * 23).round();
  }



}